import requests
from bs4 import BeautifulSoup
import time
import json
import os
from datetime import datetime, timedelta
import hashlib
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import logging
import schedule

def ensure_directories():
    """确保必要的目录存在"""
    directories = ['config', 'logs', 'data', 'docs', 'scripts', 'tests']
    for directory in directories:
        if not os.path.exists(directory):
            os.makedirs(directory)

class FishCMonitor:
    def __init__(self, config_file='config/config.json'):
        # 确保目录结构存在
        ensure_directories()
        
        self.config_file = config_file
        self.config = self.load_config()
        self.setup_logging()
        self.last_content_hash = None
        self.last_topic_count = 0
        # 在初始化时就加载之前保存的状态
        self.load_state()
        
    def load_config(self):
        """加载配置文件"""
        default_config = {
            "url": "https://fishc.com.cn/forum-400-1.html",
            "check_interval": 300,  # 检查间隔（秒）
            "schedule": {
                "enabled": True,
                "time": "09:00",
                "timezone": "Asia/Shanghai"
            },
            "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
            "email": {
                "enabled": False,
                "smtp_server": "smtp.qq.com",
                "smtp_port": 587,
                "sender_email": "",
                "sender_password": "",
                "receiver_email": ""
            },
            "wechat": {
                "enabled": False,
                "webhook_url": ""  # 企业微信机器人webhook地址
            },
            "sms": {
                "enabled": False,
                "api_key": "",
                "phone_number": ""
            }
        }
        
        if os.path.exists(self.config_file):
            with open(self.config_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
                # 合并默认配置
                for key, value in default_config.items():
                    if key not in config:
                        config[key] = value
                return config
        else:
            # 创建默认配置文件
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(default_config, f, indent=4, ensure_ascii=False)
            return default_config
    
    def setup_logging(self):
        """设置日志"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('logs/fishc_monitor.log', encoding='utf-8'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def fetch_page_content(self):
        """获取页面内容"""
        headers = {
            'User-Agent': self.config['user_agent'],
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
            'Accept-Encoding': 'gzip, deflate',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1'
        }
        
        try:
            response = requests.get(self.config['url'], headers=headers, timeout=30)
            response.raise_for_status()
            response.encoding = 'utf-8'
            return response.text
        except requests.RequestException as e:
            self.logger.error(f"获取页面内容失败: {e}")
            return None
    
    def parse_topic_count(self, html_content):
        """解析主题数量 - 基于瀑布流布局"""
        try:
            soup = BeautifulSoup(html_content, 'html.parser')
            
            # 查找瀑布流容器中的主题（这是鱼C论坛闪拍页面的实际布局）
            waterfall_container = soup.find('ul', id='waterfall')
            if waterfall_container:
                topic_items = waterfall_container.find_all('li')
                
                # 验证每个li是否确实是主题
                valid_topics = []
                for li in topic_items:
                    # 检查是否包含主题链接
                    topic_link = li.find('a', href=lambda x: x and 'thread-' in x)
                    if topic_link:
                        valid_topics.append(li)
                
                self.logger.info(f"瀑布流容器中找到 {len(topic_items)} 个项目，其中 {len(valid_topics)} 个有效主题")
                return len(valid_topics)
            
            # 备用方法: 如果没有找到瀑布流容器，使用传统方法
            self.logger.warning("未找到瀑布流容器，使用备用解析方法")
            
            # 查找tbody中的tr元素（排除表头）
            tbody = soup.find('tbody')
            if tbody:
                topic_rows = tbody.find_all('tr')
                # 过滤掉表头和其他非主题行
                valid_topics = [tr for tr in topic_rows if tr.find('a', href=lambda x: x and 'thread-' in x)]
                if valid_topics:
                    return len(valid_topics)
            
            # 最后的备用方法: 直接查找包含thread链接的元素（但这会包含重复链接）
            thread_links = soup.find_all('a', href=lambda x: x and 'thread-' in x)
            # 去重：提取唯一的thread ID
            unique_threads = set()
            for link in thread_links:
                href = link.get('href', '')
                if 'thread-' in href:
                    # 提取thread ID
                    import re
                    match = re.search(r'thread-(\d+)', href)
                    if match:
                        unique_threads.add(match.group(1))
            
            self.logger.warning(f"使用备用方法，找到 {len(thread_links)} 个链接，去重后 {len(unique_threads)} 个唯一主题")
            return len(unique_threads)
            
        except Exception as e:
            self.logger.error(f"解析主题数量失败: {e}")
            return 0
    
    def get_content_hash(self, content):
        """获取内容哈希值"""
        return hashlib.md5(content.encode('utf-8')).hexdigest()
    
    def send_email_notification(self, message):
        """发送邮件通知"""
        if not self.config['email']['enabled']:
            return False
            
        try:
            msg = MIMEMultipart()
            msg['From'] = self.config['email']['sender_email']
            msg['To'] = self.config['email']['receiver_email']
            msg['Subject'] = "鱼C论坛闪拍监控通知"
            
            msg.attach(MIMEText(message, 'plain', 'utf-8'))
            
            server = smtplib.SMTP(self.config['email']['smtp_server'], self.config['email']['smtp_port'])
            server.starttls()
            server.login(self.config['email']['sender_email'], self.config['email']['sender_password'])
            server.send_message(msg)
            server.quit()
            
            self.logger.info("邮件通知发送成功")
            return True
        except Exception as e:
            self.logger.error(f"邮件发送失败: {e}")
            return False
    
    def send_wechat_notification(self, message):
        """发送微信通知（企业微信机器人）"""
        if not self.config['wechat']['enabled']:
            return False
            
        try:
            data = {
                "msgtype": "text",
                "text": {
                    "content": message
                }
            }
            
            response = requests.post(
                self.config['wechat']['webhook_url'],
                json=data,
                headers={'Content-Type': 'application/json'}
            )
            
            if response.status_code == 200:
                self.logger.info("微信通知发送成功")
                return True
            else:
                self.logger.error(f"微信通知发送失败: {response.text}")
                return False
        except Exception as e:
            self.logger.error(f"微信通知发送失败: {e}")
            return False
    
    def send_notifications(self, message):
        """发送所有启用的通知"""
        self.send_email_notification(message)
        self.send_wechat_notification(message)
        # 可以在这里添加短信通知功能
    
    def check_changes(self):
        """检查页面变化"""
        self.logger.info("开始检查页面变化...")
        
        html_content = self.fetch_page_content()
        if not html_content:
            return
        
        current_topic_count = self.parse_topic_count(html_content)
        current_hash = self.get_content_hash(html_content)
        
        self.logger.info(f"当前主题数量: {current_topic_count}")
        
        # 检查是否有变化
        if self.last_topic_count != 0 and current_topic_count != self.last_topic_count:
            change_type = "增加" if current_topic_count > self.last_topic_count else "减少"
            change_count = abs(current_topic_count - self.last_topic_count)
            
            message = f"""鱼C论坛闪拍页面监控通知
            
时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
页面: {self.config['url']}
变化: 主题数量{change_type}了 {change_count} 个
之前数量: {self.last_topic_count}
当前数量: {current_topic_count}
            """
            
            self.logger.info(f"检测到变化: {message}")
            self.send_notifications(message)
        
        # 更新记录
        self.last_topic_count = current_topic_count
        self.last_content_hash = current_hash
        
        # 保存状态
        self.save_state()
    
    def save_state(self):
        """保存监控状态"""
        state = {
            'last_topic_count': self.last_topic_count,
            'last_content_hash': self.last_content_hash,
            'last_check_time': datetime.now().isoformat()
        }
        
        with open('data/monitor_state.json', 'w', encoding='utf-8') as f:
            json.dump(state, f, indent=4, ensure_ascii=False)
    
    def load_state(self):
        """加载监控状态"""
        if os.path.exists('data/monitor_state.json'):
            try:
                with open('data/monitor_state.json', 'r', encoding='utf-8') as f:
                    state = json.load(f)
                    self.last_topic_count = state.get('last_topic_count', 0)
                    self.last_content_hash = state.get('last_content_hash', None)
                    self.logger.info(f"加载上次状态: 主题数量 {self.last_topic_count}")
            except Exception as e:
                self.logger.error(f"加载状态失败: {e}")
    
    def get_next_check_time(self):
        """计算下次检查时间"""
        if not self.config.get('schedule', {}).get('enabled', False):
            return None
            
        target_time = self.config['schedule']['time']
        now = datetime.now()
        
        # 解析目标时间
        hour, minute = map(int, target_time.split(':'))
        
        # 计算今天的目标时间
        today_target = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
        
        # 如果今天的目标时间已过，则设置为明天
        if now >= today_target:
            next_target = today_target + timedelta(days=1)
        else:
            next_target = today_target
            
        return next_target
    
    def wait_until_next_check(self):
        """等待到下次检查时间"""
        next_check = self.get_next_check_time()
        if next_check is None:
            # 如果没有启用定时调度，使用原来的间隔方式
            self.logger.info(f"等待 {self.config['check_interval']} 秒后进行下次检查...")
            time.sleep(self.config['check_interval'])
            return
            
        now = datetime.now()
        wait_seconds = (next_check - now).total_seconds()
        
        if wait_seconds > 0:
            self.logger.info(f"下次检查时间: {next_check.strftime('%Y-%m-%d %H:%M:%S')}")
            self.logger.info(f"等待 {wait_seconds:.0f} 秒...")
            time.sleep(wait_seconds)
    
    def run(self):
        """运行监控"""
        self.logger.info("鱼C论坛闪拍监控启动")
        
        # 显示调度信息
        if self.config.get('schedule', {}).get('enabled', False):
            target_time = self.config['schedule']['time']
            self.logger.info(f"定时调度已启用，每天 {target_time} 执行检查")
            next_check = self.get_next_check_time()
            if next_check:
                self.logger.info(f"下次检查时间: {next_check.strftime('%Y-%m-%d %H:%M:%S')}")
        else:
            self.logger.info(f"使用间隔模式，每 {self.config['check_interval']} 秒检查一次")
        
        try:
            while True:
                self.check_changes()
                self.wait_until_next_check()
        except KeyboardInterrupt:
            self.logger.info("监控已停止")
        except Exception as e:
            self.logger.error(f"监控运行出错: {e}")

if __name__ == "__main__":
    monitor = FishCMonitor()
    monitor.run()